#include <lvgl.h>
#include <TFT_eSPI.h>
#include <ui.h>
#include "String.h"
#include <MYFINGER.h>
#include <HardwareSerial.h>
#include <Ticker.h>
#include <WiFi.h>
#include <Ticker.h>
#include <Keypad.h>
#include <EEPROM.h>
#include "mystack.h"
#include "myclock.h"
#include <ESP32Encoder.h>
#include "MYTCP.h"
#include "semphr.h"
#include "HTTPClient.h"
#include <AsyncUDP_ESP32_SC_ENC.h>
// #include <TaskScheduler.h>

#define u8 uint8_t
#define u16 uint16_t
#define TFT_BL 16
#define enterKey 41
#define CancelKey 42
#define EC11B 10
#define EC11A 11
// 定义EEPROM的大小
// EEPROM 区  max4096
// 0    - 1550      工作时长记录区
// 1550 - 3100      工作时长备份区
// 3100 - 3500      工作ID区
// 3500 - 3550      签到时间记录区
// 3550 - 3650      总工作时长区
// 3650 - 3750      总工作时长备份区
// 4000 - 4010      时间参数区
#define EEPROM_SIZE 4096
#define maxusernumber 40

// 用户数据

uint8_t ValidFinger = 0;
typedef struct
{
    uint8_t last_h;
    uint8_t worktime;
} uWork;
enum Screen
{
    Screen_Main,
    Screen_Select,
    Screen_Sysifo,
    Screen_Enroll,
    Screen_Close,
};

// 任务调度器
// Scheduler runner;
uWork finger_record[maxusernumber][33]; //[userID][day]
uint32_t userIDCode[maxusernumber][2];  /// 2200520318->

u8 userCheck[maxusernumber]; /// 2200520318->
u16 TotalWorkTime[maxusernumber];
// 按键定时器
Ticker keytime;
Ticker systime;

// 编码器

u8 encoderSensitive = 1;
ESP32Encoder encoder;

// 指纹模块
HardwareSerial MySerial_1(1);
uint8_t TCH = 17;
char a[] = {0xAA, 0XFB, 0xFF, 0xFF, 0x2D, 0x31, 0x32, 0x2E, 0x33, 0x34};
String str;
byte address[4] = {0xff, 0xff, 0xff, 0xff};
Finger finger = Finger(&MySerial_1, address);
// LVGL
static const uint16_t screenWidth = 240;
static const uint16_t screenHeight = 280;
QueueHandle_t TFT_Meg;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * screenHeight / 10];
TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight);
// 网络配置
#define NTP1 "ntp-sz.chl.laibing.net"
#define NTP2 "edu.ntp.org.cn"
#define NTP3 "cn.ntp.org.cn"
const char *ssid = "GUET-WiFi";
String GUETURL = "http://10.0.1.5:801/eportal/portal/login?callback=dr1003&login_method=1";
String MYcount = "2200520318";
String MYPassword = "TFdYbmI2NjY2NjY=";
String GUETlast = "&wlan_user_ipv6=&wlan_user_mac=000000000001&wlan_ac_ip=10.32.255.10&wlan_ac_name=HJ-BRAS-ME60-01&jsVersion=4.2&terminal_type=1&lang=zh-cn&v=7279&lang=zh";
const char *password = "88888888";
const char *xwssid = "ESP_LOCK";

// udp
AsyncUDP udp;                     // 创建UDP对象
unsigned int localUdpPort = 4567; // 本地端口号
// 下位机                                                                                     // 本设备IP地址
const String WDAY_NAMES[] = {"", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat", "Sun"};                                   // 星期
const String MONTH_NAMES[] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; // 月份
const u8 MONTH_DAYS[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};                                           // 月份
// TCP
mytcp tcpmag;

// 系统时间
uint16_t year;
clockTime SysClockTime;
uint8_t month, day, oldh, Wday, oldday;
char *ditime = "00:00:00";
uint8_t SysFreeTime_mm = 0;

// 全局配置
uint8_t TFT_stateFlag = 0;
uint8_t WIFI_Open_Flag = 0;
uint8_t WIFI_ReConnectFlag = 0;
Stack_Int nowscr;
uint8_t SetSaveTime = 2;
uint8_t Tft_OpenTime = 1;
bool GetNetTimeLoopFlag = 0;

// 互斥量
SemaphoreHandle_t xMutex_UiChange;   // UI互斥锁
SemaphoreHandle_t xMutex_EEprom;     // Flash互斥锁
SemaphoreHandle_t xMutex_Time;       // 时间互斥锁
SemaphoreHandle_t xMutex_userData;   // userData互斥锁
SemaphoreHandle_t xMutex_fingerData; // fingerData互斥锁
//////////////////////////////// 蜂鸣器 ///////////////////////////////////////////////

#define BeepIo 45
#define OpenBeep digitalWrite(BeepIo, HIGH)
#define CloseBeep digitalWrite(BeepIo, LOW)
uint16_t beepsw = 0;
uint16_t beeptime;
uint16_t beeptwis;
uint16_t beepinterval;
void Beeploop(void *parameter)
{
    while (1)
    {

        while (beeptwis > 0)
        {
            OpenBeep;
            vTaskDelay(beeptime / portTICK_PERIOD_MS);
            CloseBeep;
            vTaskDelay(beepinterval / portTICK_PERIOD_MS);
            beeptwis--;
        }
        if (beeptwis > 0)
            vTaskDelay(2);
        else
            vTaskDelay(1000);
    }
    vTaskDelete(NULL);
}

void setBeep(uint16_t tw, uint16_t t, uint16_t interval)
{
    beeptime = t;
    beeptwis = tw;
    beepinterval = interval;
}

const int8_t Encoder_IOA = 11;
const int8_t Encoder_IOB = 10;

//////////////////select回调函数///////////////////////////////
enum SelectMenu
{
    Select_Info,
    Select_enroll,
    Select_worktime
};
u8 Select_Choose = 0;
void ui_event_select(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e); // 获取回调值
    lv_obj_t *obj = lv_event_get_target(e);      // 获取对象
    if (code == LV_EVENT_VALUE_CHANGED)
    { // 判断当前事件
        Select_Choose = lv_roller_get_selected(obj);
        Serial.printf("Select_Choose : %d\n", Select_Choose);
    }
}

u8 Enroll_ID1 = 0, Enroll_ID2 = 0, Enroll_ID3 = 0;
void ui_event_setID1(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e); // 获取回调值
    lv_obj_t *obj = lv_event_get_target(e);      // 获取对象
    if (code == LV_EVENT_VALUE_CHANGED)
    { // 判断当前事件
        Enroll_ID1 = lv_roller_get_selected(obj);
        // EnrollScreen_shouIDNum();
    }
}
void ui_event_setID2(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e); // 获取回调值
    lv_obj_t *obj = lv_event_get_target(e);      // 获取对象
    if (code == LV_EVENT_VALUE_CHANGED)
    { // 判断当前事件
        Enroll_ID2 = lv_roller_get_selected(obj);
        // EnrollScreen_shouIDNum();
    }
}
void ui_event_setID3(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e); // 获取回调值
    lv_obj_t *obj = lv_event_get_target(e);      // 获取对象
    if (code == LV_EVENT_VALUE_CHANGED)
    { // 判断当前事件
        Enroll_ID3 = lv_roller_get_selected(obj);
        // EnrollScreen_shouIDNum();
    }
}
u16 Enrrol_ID = 0;
u16 oldEnrrol_ID = 0;
void EnrollScreen_shouIDNum()
{
    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
    {
        Enroll_ID1 = lv_roller_get_selected(ui_EnrollID1);
        Enroll_ID2 = lv_roller_get_selected(ui_EnrollID2);
        Enroll_ID3 = lv_roller_get_selected(ui_EnrollID3);
        Enrrol_ID = Enroll_ID1 * 100 + Enroll_ID2 * 10 + Enroll_ID3;
        if (oldEnrrol_ID != Enrrol_ID)
        {
            if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
            {
                if (Enrrol_ID < maxusernumber)
                    lv_label_set_text_fmt(ui_LabEnrollShow, "%d%d", userIDCode[Enrrol_ID][0], userIDCode[Enrrol_ID][1]);
                else
                    lv_label_set_text_fmt(ui_LabEnrollShow, "xxxxxxxxxx");
                xSemaphoreGive(xMutex_userData);
            }

            oldEnrrol_ID = Enrrol_ID;
        }
        xSemaphoreGive(xMutex_UiChange);
    }
}
#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char *buf)
{
    Serial.printf(buf);
    Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors((uint16_t *)&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}

uint16_t touchX = 0, touchY = 0, touchTime = 0, touchDelay;

bool touched = false; // tft.getTouch( &touchX, &touchY, 600 );
/*Read the touchpad*/
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{

    if (!touched)
    {
        data->state = LV_INDEV_STATE_REL;
    }
    else
    {
        data->state = LV_INDEV_STATE_PR;
        data->point.x = touchX;
        data->point.y = touchY;
    }
}
void connectToGuet()
{
    WiFi.begin(ssid);
    WiFi.setSleep(false);
    while (WiFi.status() != WL_CONNECTED)
    {
        WiFi.begin(ssid);
        delay(1000);
        Serial.print("...");
    }
    Serial.print("WIFI Connected");
    // TCP 初始化
    // WiFi.localIP().toString()
    HTTPClient http;
    http.begin(GUETURL + "&user_account=,0," + MYcount + "&user_password=" + MYPassword + "&wlan_user_ip=" + WiFi.localIP().toString() + GUETlast);
    int httpcode = http.GET();
    Serial.printf("httpcode %d \n", httpcode);
    Serial.println(http.getString());
    http.end();

    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
    {
        lv_label_set_text(ui_Label6, ssid);
        lv_label_set_text(ui_Label7, password);
        lv_label_set_text(ui_Label8, WiFi.localIP().toString().c_str());
        Serial.println("IP ");
        Serial.println(WiFi.localIP().toString());
        xSemaphoreGive(xMutex_UiChange);
    }
}
u8 esplockFlag = 0;
void onPacketCallBack(AsyncUDPPacket packet)
{
    Serial.print("UDP数据包来源类型: ");
    Serial.println(packet.isBroadcast() ? "广播数据" : (packet.isMulticast() ? "组播" : "单播"));
    Serial.print("远端地址及端口号: ");
    Serial.print(packet.remoteIP());
    Serial.print(":");
    Serial.println(packet.remotePort());
    Serial.print("目标地址及端口号: ");
    Serial.print(packet.localIP());
    Serial.print(":");
    Serial.println(packet.localPort());
    Serial.print("数据长度: ");
    Serial.println(packet.length());
    Serial.print("数据内容: ");
    Serial.write(packet.data(), packet.length());
    Serial.println();

    packet.print("reply data");
    // broadcastPort = packet.remotePort();
}

StopWatch testtic;
StopWatch Tic_WIFIFree;
StopWatch Tic_TFTFree;
StopWatch LOCKtic;
void setup()
{
    // 互斥锁初始化
    xMutex_UiChange = xSemaphoreCreateMutex();
    xMutex_Time = xSemaphoreCreateMutex();
    xMutex_fingerData = xSemaphoreCreateMutex();
    xMutex_userData = xSemaphoreCreateMutex();
    xMutex_EEprom = xSemaphoreCreateMutex();

    TFT_Close();
    Serial.begin(); /* prepare for possible serial debug */
    Serial.setDebugOutput(true);
    Serial.println("Init::begin load sys!\n");
    String LVGL_Arduino = "Hello Arduino! ";
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    Serial.println(LVGL_Arduino);
    Serial.println("Init::I am LVGL_Arduino");

    lv_init();

    // 定义EEPROM大小
    EEPROM.begin(EEPROM_SIZE);
    // #if LV_USE_LOG != 0
    //     lv_log_register_print_cb(my_print); /* register print function for debugging */
    // #endif

    tft.begin();        /* TFT init */
    tft.setRotation(0); /* Landscape orientation, flipped */

    lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * screenHeight / 10);

    /*Initialize the display*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    /*Change the following line to your display resolution*/
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    /*Initialize the (dummy) input device driver*/
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register(&indev_drv);

    // ui初始化
    ui_init();
    Tic_TFTFree.tic();
    pinMode(BeepIo, OUTPUT);
    pinMode(enterKey, INPUT_PULLUP);
    pinMode(CancelKey, INPUT_PULLUP);
    // 启动线程
    Serial.println("Init::Begin init_thread");
    // WIFI 初始化ssid, password
    WiFi.mode(WIFI_MODE_STA);
    Serial.println("Init::Begin init_WIFI");
    // WiFi.begin(ssid, password);
    // WiFi.setSleep(false);
    // u8 times = 5;

    // while (times--)
    // {
    //     WiFi.begin(ssid, password);
    //     if (WiFi.status() == WL_CONNECTED)
    //         break;
    //     Serial.println("Connecting...");
    //     delay(1000);
    // }
    // delay(1000);

    // // TCP 初始化
    // Serial.println("Begin init TCP ");
    // if (WiFi.status() == WL_CONNECTED)
    // {
    //     Serial.println("Wifi connect");
    //     tcpmag.init();
    //     tcpmag.begin();
    //     Serial.println("IP ");
    //     Serial.println(WiFi.localIP());
    // }
    Serial.println("Init::Begin init TCP ");

    // IPAddress local_IP(192, 168, 4, 1);
    // IPAddress gateway(192, 168, 4, 1);
    // IPAddress subnet(255, 255, 255, 0);
    // // 开启热点
    // WiFi.softAP(xwssid, password);
    // WiFi.softAPConfig(local_IP, gateway, subnet);
    // Serial.print("AP IP address: ");
    // Serial.println(WiFi.softAPIP());

    connectToGuet();
    tcpmag.init();
    tcpmag.begin();
    // 网络时间同步
    Serial.println("Init::Begin init Time");
    init_systime();
    WifiStart();
    // 定时器初始化
    Serial.println("Init::Begin init TimerTic");
    keytime.attach_ms(5, keyscan);
    systime.attach_ms(20, syssettime);
    // 线程初始化
    Serial.println("Init::Begin init_thread");
    init_thread();
    // GPIOinit
    pinMode(TCH, INPUT_PULLDOWN);
    // // 编码器init
    ESP32Encoder::useInternalWeakPullResistors = puType::up;
    encoder.attachHalfQuad(Encoder_IOA, Encoder_IOB);
    encoder.setCount(0);
    // pinMode(Encoder_IOA, INPUT_PULLUP);
    // pinMode(Encoder_IOB, INPUT_PULLUP);
    Serial.println("Init::MySerial_1.begin");
    //  串口init
    MySerial_1.begin(57600, SERIAL_8N1, 8, 18);

    randomSeed(analogRead(0));

    ValidFinger = finger.Get_ValidTempleteNum(); // 测试指纹通讯
    if (ValidFinger <= 0XF0)
        Serial.printf("\n Init::finger Setup done %d\n", ValidFinger);
    else
        Serial.printf("\n Init::finger Setup Error %d \n", ValidFinger);
    finger.ReadSysPara();
    nowscr.push(Screen_Main);
    setBeep(3, 100, 50); // 短响
    Serial.println("Init::Setup done");
    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
    {
        lv_label_set_text_fmt(ui_Label4, "%02d", nowscr.top());
        lv_label_set_text(ui_Label6, ssid);
        lv_label_set_text(ui_Label7, password);
        lv_label_set_text(ui_Label8, WiFi.localIP().toString().c_str());
        xSemaphoreGive(xMutex_UiChange);
    }
    // 读取eeprom
    Serial.println("Init::Begin read_EEProm");
    read_EEProm();
    testtic.tic();

    // print_data();
    // testDelay.tic();
    //  OpenBeep;
    TFT_Open();
}
void loop()
{
    if (Serial.available())
    {
        char a = Serial.read();
        if (a == '1')
        {
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                for (uint8_t d = 0; d < 32; d++)
                {
                    finger_record[i][d].worktime = i + d;
                }
            }
            print_worktime();
        }
        if (a == '2')
        {
            save_checkinFlash(1, 15);
            save_checkinFlash(2, 15);
            save_checkinFlash(3, 15);
        }
        if (a == '3')
        {
            syn_WorkTotalTime();
            CheckOut_All_User();
        }
        if (a == '4')
        {
            Serial.printf("\nprint_worktime \n");
            print_worktime();
            Serial.printf("\nuserID \n");
            print_userIDCode();
            Serial.printf("\nTotalWorkTime \n");
            print_TotalWorkTime();
        }
        if (a == '5') // 十七号测试
        {
            save_checkinFlash(22, 19);
            save_checkinFlash(1, 19);
            save_checkinFlash(2, 19);
            save_checkinFlash(3, 19);
            finger_record[0][17].worktime = 2;
            save_EEProm(0);
            oldday = day;
        }

        if (a == '6')
        {
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                for (uint8_t d = 0; d < 32; d++)
                {
                    finger_record[i][d].worktime = 0;
                }
            }
        }

        if (a == '7')
        {
            SysClockTime.hh++;
            Serial.printf("now time is %d-%d-%d : %d : %d : % \n", year, month, day, SysClockTime.hh, SysClockTime.mm, SysClockTime.ss);
            Serial.printf("old_time is %d :  \n", oldh);
        }

        if (a == '8')
        {
            SysClockTime.ss += 45;
            Serial.printf("now time is %d-%d-%d : %d : %d : % \n", year, month, day, SysClockTime.hh, SysClockTime.mm, SysClockTime.ss);
        }
        if (a == '9')
        {
            WIFI_Open_Flag = 1;
            esplockFlag = 1;
            while (!udp.listen(localUdpPort)) // 等待udp监听设置成功
            {
            }
            Serial.printf("UDP Listening\n");
            udp.onPacket(onPacketCallBack); // 注册收到数据包事件
            LOCKtic.tic();
        }
        if (a == 'A')
        {
            finger.EmptyChar();
        }
    }
    FingerIdentify();        // 指纹验证
    fingerEnroll();          // 指纹注册
    sendUserWorkData();      // TCP发送工作时长
                             // OpenLock();
    if (testtic.toc() > 500) // 500ms 判断一次有无手指按下
    {
        testtic.tic();
        // Serial.println(digitalRead(TCH));
        if (finger.GetImage() == 0)
            TCH = 1;
        else
            TCH = 2;
    }
    // put your main code here, to run repeatedly:
}

void OpenLock()
{
    static u16 outtime = 0;
    if (esplockFlag == 1) // 连接下位机
    {
        WiFi.mode(WIFI_MODE_STA);
        WiFi.disconnect();
        WiFi.begin(xwssid, password);
        esplockFlag = 2;
        outtime = 0;
    }
    if (esplockFlag == 2)
    {
        if (WiFi.status() != WL_CONNECTED)
        {
            if (LOCKtic.toc() > 500)
            {
                WiFi.begin(xwssid, password);
                LOCKtic.tic();
                outtime += 500;
            }
        }
        else
        {
            LOCKtic.tic();
            esplockFlag = 3;
            Serial.println("IP ");
            Serial.println(WiFi.localIP());
        }
    }

    if (esplockFlag == 3) // 发送UDP指令
    {
        if (LOCKtic.toc() > 500)
        {
            outtime += 500;
            udp.broadcast("Anyone here?");
            LOCKtic.tic();
            Serial.printf("LOCK::Send data");
        }
        // if () // 接收UDP回应
    }

    if (outtime > 10000) // 超时处理
    {
        Serial.printf("LOCK::Wait OverTime");
        WifiStart();
        udp.close();
        esplockFlag = 0;
    }
}
void connectNet()
{
    u8 times = 5;
    WiFi.begin(ssid);
    while (times--)
    {
        // connectToGuet();
        if (WiFi.status() == WL_CONNECTED)
            break;
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
    vTaskDelay(1000 / portTICK_PERIOD_MS);

    // TCP 初始化
    if (WiFi.status() == WL_CONNECTED)
    {
        // WifiStart();
        if (WIFI_ReConnectFlag == 0)
        {
            Serial.println("\n connectNet:: Connect Succeed \n");
            updatatime();
            connectToGuet();
            WIFI_ReConnectFlag = 1;
        }
    }
    else
    {
        WIFI_ReConnectFlag = 0;
    }
}
void updatatime()
{
    if (GetNetTimeLoopFlag)
    {
        Serial.println("\n updatatime Runing");
        return;
    }
    Serial.println("\nupdatatime::beging update time \n");
    BegingetNetTime();
}
void BegingetNetTime()
{
    xTaskCreate(
        getNetTime,   /*任务函数*/
        "getNetTime", /*带任务名称的字符串*/
        2048,         /*堆栈大小，单位为字节*/
        NULL,         /*作为任务输入传递的参数*/
        2,            /*任务的优先级*/
        NULL);
}
void getNetTime(void *parameter)
{
    uint8_t outtimes = 5;
    uint16_t tempyear;
    GetNetTimeLoopFlag = 1;
    struct tm timeInfo;
    while (outtimes--)
    {
        if (WiFi.status() != WL_CONNECTED)
        {
            vTaskDelay(10000 / portTICK_PERIOD_MS);
            outtimes = 5;
            continue;
        }
        Serial.println("\n Begin Get Net Time...");
        configTime(8 * 3600, 0, NTP1, NTP2, NTP3);

        getLocalTime(&timeInfo);
        vTaskDelay(100 / portTICK_PERIOD_MS);
        getLocalTime(&timeInfo);
        vTaskDelay(100 / portTICK_PERIOD_MS);
        getLocalTime(&timeInfo);
        vTaskDelay(100 / portTICK_PERIOD_MS);
        tempyear = 1900 + timeInfo.tm_year;
        if (tempyear >= 2024)
        {
            Serial.println("\n Get Net Time");
            break;
        }
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }

    if (tempyear >= 2024)
    {
        if (xSemaphoreTake(xMutex_Time, portMAX_DELAY) == pdPASS)
        {
            year = tempyear;
            month = timeInfo.tm_mon + 1;
            day = timeInfo.tm_mday;
            SysClockTime.hh = timeInfo.tm_hour;
            SysClockTime.mm = timeInfo.tm_min;
            SysClockTime.ss = timeInfo.tm_sec;
            Wday = timeInfo.tm_wday;
            Serial.printf("getNetTime:: \n %d-%d-%d : %d : %d : %d", year, month, day, SysClockTime.hh, SysClockTime.mm, SysClockTime.ss);
            xSemaphoreGive(xMutex_Time);
        }
        WifiStart();
        save_Time();
    }

    GetNetTimeLoopFlag = 0;
    vTaskDelete(NULL);
}
void init_systime()
{
    if (xSemaphoreTake(xMutex_Time, portMAX_DELAY) == pdPASS)
    {
        SysClockTime.hh = 12;
        SysClockTime.mm = 0;
        SysClockTime.ss = 0;
        SysClockTime.ms = 0;
        xSemaphoreGive(xMutex_Time);
    }
    updatatime();
    if (year >= 2024)
        save_Time();
    else
    {
        read_Time();
        if (xSemaphoreTake(xMutex_Time, portMAX_DELAY) == pdPASS)
        {
            SysClockTime.mm += 2;
            oldh = SysClockTime.hh;
            String buf = "";
            buf += MONTH_NAMES[month];
            buf += ".";
            buf += day;
            Serial.printf("init_systime:: \n %d-%d-%d : %d : %d : %d", year, month, day, SysClockTime.hh, SysClockTime.mm, SysClockTime.ss);
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_label_set_text(ui_mainTime2, buf.c_str());
                lv_label_set_text_fmt(ui_mainTime, "%02d:%02d:%02d", SysClockTime.hh, SysClockTime.mm, SysClockTime.ss);
                xSemaphoreGive(xMutex_UiChange);
            }
            xSemaphoreGive(xMutex_Time);
        }
    }
}

// 判断俩个 小时  时间间隔是否大于 hoursThreshold
bool isTime_interval(int time1, int time2, int hoursThreshold)
{
    // time2 >time1
    //  计算两个时间之间的最小绝对差异（考虑0-23的循环）
    int difference = std::min(std::abs(time2 - time1), 24 - std::abs(time2 - time1));
    // 返回差异是否大于阈值
    return difference >= hoursThreshold;
}
void WifiStart()
{
    Tic_WIFIFree.tic(); // 重新计时15分钟
    if (WIFI_Open_Flag == 0)
        return;
    WiFi.setSleep(false);
    delay(500);
    WiFi.mode(WIFI_MODE_STA);
    WIFI_Open_Flag = 0; // 重新开启WIFI 重连会自动校准时间
    WIFI_ReConnectFlag = 0;
    Serial.printf("\n WIFI RST OPEN! \n");
}
u8 old_ss = 1;
void TimerTask(void *parameter)
{
    u8 timerH = 0;
    Serial.printf("\nTimerTask:: Begin TimerTasl \n");
    while (true)
    {

        if (timerH == 2) // 每隔2小时
        {
            timerH = 0;
            // 每隔2小时同步一次时间
            if (WIFI_ReConnectFlag) // 没有重连时
                updatatime();

            Serial.printf("\nTimerTask::sys time UPdata\n");

            save_EEProm(0);          // 保存工作时长
            save_BK_TotalWorkTime(); // 保存总工作时长备份
            save_WorkTime_BK();      // 保存单日工作时长备份
        }

        if (oldh != SysClockTime.hh)
        {
            if (year >= 2024 && SysClockTime.hh == 0)
            {
                Serial.printf("TimerTask::new day!\n");
                day++;
            }
            oldh = SysClockTime.hh;
            timerH++;
            save_Time();
        }

        if (old_ss != SysClockTime.ss)
        {

            // lv_label_set_text_fmt(ui_mainTime2, "%02d", (int)encoder.getCount());
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_label_set_text_fmt(ui_mainTime, "%02d:%02d:%02d", SysClockTime.hh, SysClockTime.mm, SysClockTime.ss);
                xSemaphoreGive(xMutex_UiChange);
            }

            old_ss = SysClockTime.ss;
        }
        if (oldday != day)
        {
            String buf = "";
            buf += MONTH_NAMES[month];
            buf += ".";
            buf += day;
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_label_set_text(ui_mainTime2, buf.c_str());
                xSemaphoreGive(xMutex_UiChange);
            }
            save_Time(); // 更新oldday
            // 指纹结算
            Finger_record_settlement();
            oldday = day;
        }
        if (TFT_stateFlag && Tic_TFTFree.toc() > Tft_OpenTime * 60 * 1000)
        {
            TFT_Close();
            //  finger.SetLedState(lEDMOD_Close, 0, 0, 1);
            Serial.printf("\nTFT CLOSE! \n");
        }

        vTaskDelay(900 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

void syssettime() // 回调函数 20ms
{

    clocklLoop(&SysClockTime, 20);

    if (WIFI_Open_Flag == 0 && Tic_WIFIFree.toc() > 900000) // 无TCP业务空闲15分钟后
    {
        Tic_WIFIFree.tic();
        //  Serial.printf("\n WIFI Close !\n");
        //    WIFI_Open_Flag = 1;
    }
}

// 多线程初始化
void init_thread()
{

    xTaskCreate(
        TCP_Message, /*任务函数*/
        "TCP_meg",   /*带任务名称的字符串*/
        2048,        /*堆栈大小，单位为字节*/
        NULL,        /*作为任务输入传递的参数*/
        4,           /*任务的优先级*/
        NULL);
    xTaskCreate(
        TCP_Send,   /*任务函数*/
        "TCP_Send", /*带任务名称的字符串*/
        2048,       /*堆栈大小，单位为字节*/
        NULL,       /*作为任务输入传递的参数*/
        4,          /*任务的优先级*/
        NULL);
    xTaskCreate(
        TFT_Draw,   /*任务函数*/
        "TFT_Draw", /*带任务名称的字符串*/
        8192,       /*堆栈大小，单位为字节*/
        NULL,       /*作为任务输入传递的参数*/
        2,          /*任务的优先级*/
        NULL);
    xTaskCreate(
        Beeploop,   /*任务函数*/
        "Beeploop", /*带任务名称的字符串*/
        2048,       /*堆栈大小，单位为字节*/
        NULL,       /*作为任务输入传递的参数*/
        1,          /*任务的优先级*/
        NULL);
    xTaskCreate(
        TimerTask,   /*任务函数*/
        "TimerTask", /*带任务名称的字符串*/
        2048,        /*堆栈大小，单位为字节*/
        NULL,        /*作为任务输入传递的参数*/
        1,           /*任务的优先级*/
        NULL);
}

Stack_ButtonPass touch;
// 按键
void click(uint16_t x, uint16_t y)
{
    touch.push(ButtonPass(x, y, 20, 0));
}

void Longclick(uint16_t x, uint16_t y)
{
    touch.push(ButtonPass(x, y, 300, 0));
}

void SlideclickX(uint16_t x, uint16_t tx, u8 time)
{
}

void Delayclick(uint16_t x, uint16_t y, uint16_t delay)
{
    touch.push(ButtonPass(x, y, 600, delay));
}
void clickEven(u16 time)
{
    if (touchDelay != 0)
    {
        if (touchDelay >= time)
            touchDelay -= time;
        else
            touchDelay = 0;
        return;
    }
    if (touchTime > 0)
    {
        if (touchTime >= time)
            touchTime -= time;
        else
            touchTime = 0;
        touched = true;
    }
    else
    {
        if (touch.size() != 0)
        {
            ButtonPass temp = touch.top();
            touch.pop();
            touchX = temp.x;
            touchY = temp.y;
            touchTime = temp.time;
            touchDelay = temp.delay;
        }
        touched = false;
    }
}

KeyPass enterPass(0, 0);
KeyPass cancelPass(0, 0);
enum KeyPrass
{
    Key_none = 0,
    Key_click,
    Key_long
};

u8 EncoderMove; // 1 up 2 dwon
void readKey(u8 time)
{

    // int NowEncoder = 0;
    int NowEncoder = encoder.getCount();
    if (digitalRead(enterKey) == 0)
    {
        enterPass.KeyTime += time;
    }
    else
    {
        if (enterPass.KeyTime < 100)
            enterPass.KeyType = Key_none;
        else if (enterPass.KeyTime > 100 && enterPass.KeyTime < 500)
            enterPass.KeyType = Key_click;
        else
            enterPass.KeyType = Key_long;

        if (enterPass.KeyTime > 100)
            Serial.printf("enterPass type:%d nowscr %d\n", enterPass.KeyType, nowscr.top());

        if (enterPass.KeyType != Key_none)
            TFT_Open();
        enterPass.KeyTime = 0;
    }

    if (digitalRead(CancelKey) == 0)
    {
        cancelPass.KeyTime += time;
    }
    else
    {
        if (cancelPass.KeyTime < 10)
            cancelPass.KeyType = Key_none;
        else if (cancelPass.KeyTime > 10 && cancelPass.KeyTime < 500)
            cancelPass.KeyType = Key_click;
        else
            cancelPass.KeyType = Key_long;
        if (cancelPass.KeyTime > 100)
            Serial.printf("cancelPass type:%d nowscr %d\n", cancelPass.KeyType, nowscr.top());
        if (enterPass.KeyType != Key_none)
            TFT_Open();
        cancelPass.KeyTime = 0;
    }

    if (NowEncoder > encoderSensitive)
    {
        EncoderMove = 1; // 向上
        Serial.printf("up %d\n", NowEncoder);
        encoder.clearCount();
    }
    else if (NowEncoder < -encoderSensitive)
    {
        EncoderMove = 2; // 向向下
        Serial.printf("down %d \n", NowEncoder);
        encoder.clearCount();
    }
}

u8 oldscr = 0;
u8 select_num = 0;
u8 fingerlock = 0;
// 5ms
void keyscan()
{
    clickEven(5);
    readKey(5);
    if (nowscr.top() == Screen_Main) // 主界面且按下确认键，唤出菜单
    {
        if (enterPass.KeyType == Key_click)
        {
            nowscr.push(Screen_Select);
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_ButtonSelect, LV_EVENT_CLICKED, NULL);
                xSemaphoreGive(xMutex_UiChange);
            }
            // click(37, 265); // 唤出菜单
            enterPass.KeyType = Key_none;
        }

        if (cancelPass.KeyType == Key_long)
        {
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
                lv_obj_add_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN);
                xSemaphoreGive(xMutex_UiChange);
            }
        }
    }
    else if (nowscr.top() == Screen_Select)
    {
        if (enterPass.KeyType == Key_long)
        {
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                Select_Choose = lv_roller_get_selected(ui_SELECT);
                xSemaphoreGive(xMutex_UiChange);
            }

            Serial.printf("Select_Choose %d\n", Select_Choose);
            switch (Select_Choose)
            {
            case Select_Info:
                nowscr.pop();
                nowscr.push(Screen_Sysifo); // 进入系统信息界面
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_event_send(ui_ButtonSysIFO, LV_EVENT_CLICKED, NULL); // 播放动画
                    xSemaphoreGive(xMutex_UiChange);
                }
                break;
            case Select_enroll:
                fingerlock = 0;
                for (u8 i = 0; i < MAX_CLIENTS; i++)
                {
                    if (tcpmag.topClient[i].client.connected() && tcpmag.topClient[i].Identification == 5) // ID为5的主机存在
                        fingerlock = 1;
                }
                if (fingerlock || 1)
                {
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_event_send(ui_SELECT, LV_EVENT_LONG_PRESSED, NULL);
                        lv_event_send(ui_ButtonCheck2, LV_EVENT_CLICKED, NULL);
                        xSemaphoreGive(xMutex_UiChange);
                    }
                    nowscr.pop();
                    nowscr.push(Screen_Enroll); // 进入系统信息界面
                }
                break;
            case Select_worktime:
                WifiStart();
                break;
            }
            enterPass.KeyType = Key_none;
        }

        if (cancelPass.KeyType >= Key_click)
        {
            nowscr.pop();
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_SELECT, LV_EVENT_LONG_PRESSED, NULL);
                xSemaphoreGive(xMutex_UiChange);
            }

            cancelPass.KeyType = Key_none;
        }
        if (EncoderMove == 1) // up
        {
            if (select_num > 0)
                select_num--;
            else
                select_num = 2;
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_roller_set_selected(ui_SELECT, select_num, LV_ANIM_ON);
                xSemaphoreGive(xMutex_UiChange);
            }

            EncoderMove = 0;
        }
        else if (EncoderMove == 2)
        {
            if (select_num < 2)
                select_num++;
            else
                select_num = 0;
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_roller_set_selected(ui_SELECT, select_num, LV_ANIM_ON);
                xSemaphoreGive(xMutex_UiChange);
            }

            EncoderMove = 0;
        }
    }
    else if (nowscr.top() == Screen_Sysifo)
    {
        if (enterPass.KeyType >= Key_click || cancelPass.KeyType >= Key_click)
        {
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_Panel3, LV_EVENT_CLICKED, NULL);
                xSemaphoreGive(xMutex_UiChange);
            }

            nowscr.pop();
            cancelPass.KeyType = Key_none;
            enterPass.KeyType = Key_none;
        }
    }
    else if (nowscr.top() == Screen_Close)
    {
        if (enterPass.KeyType >= Key_click || cancelPass.KeyType >= Key_click)
        {
            TFT_Open();
            cancelPass.KeyType = Key_none;
            enterPass.KeyType = Key_none;
        }
    }
    if (oldscr != nowscr.top())
    {
        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
        {
            lv_label_set_text_fmt(ui_Label4, "%02d", nowscr.top());
            xSemaphoreGive(xMutex_UiChange);
        }

        if (oldscr == Screen_Close) // 从屏幕休眠唤醒时
        {

            SysFreeTime_mm = SysClockTime.mm;
        }
        oldscr = nowscr.top();
    }
}

// 指纹模块
uint8_t fingercm = 1;

StopWatch fingerDelay;
void FingerIdentify()
{
    static u8 IdentifyFlag = 0;
    if (IdentifyFlag == 0)
    {
        if ((nowscr.top() == Screen_Main || nowscr.top() == Screen_Close) && TCH == 1 && TCH == fingercm)
        { /// 处于空闲界面并有指纹按下时
            if (finger.GetImage() != 0)
                return;
            delay(5);
            finger.GenChar(1);
            struct FMatch thisMatch(0, 0, 0);
            thisMatch = finger.Search(1, 0, maxusernumber);
            fingercm = 0;
            Serial.printf("check in:ID %d Score %d \n", thisMatch.PageID, thisMatch.MaScore);
            if (thisMatch.CheckCode == 0 && thisMatch.MaScore > 800)
            {
                Serial.printf("finger in \n");
                if (!TFT_stateFlag)
                {
                    TFT_Open();
                    delay(100);
                }
                IdentifyFlag = 1;
                fingerDelay.tic();
                if (!userCheck[thisMatch.PageID]) // id 登入
                {

                    // 找到指纹
                    Finger_record_worktime(thisMatch.PageID, userCheck[thisMatch.PageID]); // 更新打卡时间 ,  签到
                    Serial.printf("FingerIdentify:: xMutex_UiChange in\n");
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_label_set_text(ui_LabCheckIFO, "Check IN");
                        Serial.printf("FingerIdentify:: xMutex_userData in\n");
                        if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabStudyID, "%d%d", userIDCode[thisMatch.PageID][0], userIDCode[thisMatch.PageID][1]);
                            xSemaphoreGive(xMutex_userData);
                        }
                        Serial.printf("FingerIdentify:: xMutex_userData out\n");
                        lv_label_set_text_fmt(ui_LabWorkTime, "work TIME: %dH", TotalWorkTime[thisMatch.PageID]);
                        lv_event_send(ui_ButtonCheck, LV_EVENT_CLICKED, NULL); // 播放指纹动画
                        xSemaphoreGive(xMutex_UiChange);
                    }
                    Serial.printf("FingerIdentify:: xMutex_UiChange out\n");

                    userCheck[thisMatch.PageID] = 1; // 表示签入
                    return;
                }
                if (userCheck[thisMatch.PageID]) // id 登出
                {

                    // 找到指纹
                    Finger_record_worktime(thisMatch.PageID, userCheck[thisMatch.PageID]); // 更新打卡时间 ,  签退
                    save_workTime(thisMatch.PageID, day);
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_label_set_text(ui_LabCheckIFO, "Check OUT");

                        if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabStudyID, "%d%d", userIDCode[thisMatch.PageID][0], userIDCode[thisMatch.PageID][1]);
                            xSemaphoreGive(xMutex_userData);
                        }

                        lv_label_set_text_fmt(ui_LabWorkTime, "work TIME: %dH", TotalWorkTime[thisMatch.PageID]);
                        lv_event_send(ui_ButtonCheck, LV_EVENT_CLICKED, NULL); // 播放指纹动画
                        xSemaphoreGive(xMutex_UiChange);
                    }
                    userCheck[thisMatch.PageID] = 0; // 表示签出
                    return;
                }
            }
        }

        if (TCH == 2 && fingercm == 0) // 抬起 时间超 500ms
        {
            fingercm = 1;
        }
    }

    if (IdentifyFlag == 1) // 等待
    {
        if (fingerDelay.toc() > 3000)
        {
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
                xSemaphoreGive(xMutex_UiChange);
            }
            IdentifyFlag = 0;
        }
    }
}

//////////////////////////指纹注册//////////////////////////////////////
uint8_t fingerSetmod, oldfingerSetmod, fingeragainflag;
enum EnrollLoop
{
    Enroll_Null = 0,
    Enroll_SetID1,
    Enroll_SetID2,
    Enroll_SetID3,
    Enroll_Begin,
    Enroll_Delet
};

enum Enrollstep
{
    Enroll_step_0 = 0,
    Enroll_step_1,
    Enroll_step_2,
    Enroll_step_3,
};

enum Enroll_Delet_step
{
    Enroll_Delet_step_0 = 0,
    Enroll_Delet_step_1,
    Enroll_Delet_step_2,
};

u8 Enroll_Times = 5;
u8 Enroll_nowCS = 0;
void fingerEnroll()
{ // lv_obj_set_height(obj, 500);
    static u8 Enroll_flag = 0, Enroll_process_flag = 0, Enroll_select = 0, OldEnroll_select = 0;
    static u8 Enroll_IDNum1 = 0, Enroll_Raise_flag = 0, Enroll_Bar_var = 0, Enroll_Delet_flag = 0;
    u8 res = 0; // 指纹函数返回值
    static u8 oldtime = 0;
    if (nowscr.top() == Screen_Enroll)
    {

        if (abs(SysClockTime.ss - oldtime) > 3)
        {
            Serial.printf("Enroll_flag %d Enroll_select %d \n", Enroll_flag, Enroll_select);
            oldtime = SysClockTime.ss;
        }

        if (Enroll_flag == Enroll_Null)
        {
            // 此时，编码器选自ID位号，短按确认键进入数字调节
            // 长按确认键进入指纹录入，ID号为各位号所示
            //
            if (enterPass.KeyType == Key_click)
            {
                switch (Enroll_select)
                {
                case 1:
                    Enroll_flag = Enroll_SetID1;
                    break;
                case 2:
                    Enroll_flag = Enroll_SetID2;
                    break;
                case 3:
                    Enroll_flag = Enroll_SetID3;
                    break;
                }
                enterPass.KeyType = Key_none;
            }
            else if (enterPass.KeyType == Key_long) // 开始录入
            {
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_label_set_text_fmt(ui_LabFingerLine1, "Finger");
                    lv_label_set_text_fmt(ui_LabFingerLine2, "Press");
                    lv_bar_set_value(ui_BarEnroll, 0, LV_ANIM_OFF);             // 设置初始值
                    lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL); // 点击屏幕，播放动画
                    xSemaphoreGive(xMutex_UiChange);
                }

                Enroll_select = 0; // 初始化
                OldEnroll_select = 1;
                Enroll_process_flag = Enroll_step_0; // 录入步骤1
                Enroll_Raise_flag = 1;               // 设置为按下检测
                Enroll_flag = Enroll_Begin;          // 设为开始录入
                Enroll_Bar_var = 0;                  // 进度条清0
                Enroll_nowCS = Enroll_Times;
                EnrollScreen_shouIDNum();
                enterPass.KeyType = Key_none;
            }
            if (cancelPass.KeyType == Key_long) // 返回上一级
            {
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_event_send(ui_ButtonTOMain, LV_EVENT_CLICKED, NULL);
                    xSemaphoreGive(xMutex_UiChange);
                }

                nowscr.pop();
                cancelPass.KeyType = Key_none;
            }
            else if (cancelPass.KeyType == Key_click) // 删除指纹
            {
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_label_set_text_fmt(ui_LabFingerLine1, "Finger");
                    lv_label_set_text_fmt(ui_LabFingerLine2, "Delet?");
                    Enroll_Bar_var = 25;
                    lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_OFF); // 设置初始值
                    lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);  // 播放面板动画
                    xSemaphoreGive(xMutex_UiChange);
                }

                Enroll_flag = Enroll_Delet;
                Enroll_Delet_flag = Enroll_Delet_step_0; // 设为开始删除
                cancelPass.KeyType = Key_none;
                return;
            }
            if (OldEnroll_select != Enroll_select)
            {
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    switch (Enroll_select)
                    {
                    case 0:
                        lv_obj_set_height(ui_EnrollID1, 100);
                        lv_obj_set_height(ui_EnrollID2, 100);
                        lv_obj_set_height(ui_EnrollID3, 100);
                        break;
                    case 1:
                        lv_obj_set_height(ui_EnrollID1, 150);
                        lv_obj_set_height(ui_EnrollID2, 100);
                        lv_obj_set_height(ui_EnrollID3, 100);
                        break;
                    case 2:
                        lv_obj_set_height(ui_EnrollID1, 100);
                        lv_obj_set_height(ui_EnrollID2, 150);
                        lv_obj_set_height(ui_EnrollID3, 100);
                        break;
                    case 3:
                        lv_obj_set_height(ui_EnrollID1, 100);
                        lv_obj_set_height(ui_EnrollID2, 100);
                        lv_obj_set_height(ui_EnrollID3, 150);
                        break;
                    };
                    xSemaphoreGive(xMutex_UiChange);
                }
                OldEnroll_select = Enroll_select;
            }
            if (EncoderMove == 2)
            {
                Enroll_select += 1;
                if (Enroll_select > 3)
                    Enroll_select = 1;
                EncoderMove = 0;
            }
            else if (EncoderMove == 1)
            {
                Enroll_select -= 1;
                if (Enroll_select < 1)
                    Enroll_select = 3;
                EncoderMove = 0;
            }
        }
        else if (Enroll_flag == Enroll_SetID1)
        { // ID位号数字调节，按任意键返回上一层选择位号
            if (enterPass.KeyType == Key_click || cancelPass.KeyType == Key_click)
            {
                Enroll_select = 0;
                Enroll_flag = Enroll_Null;
                cancelPass.KeyType = Key_none;
                enterPass.KeyType = Key_none;
                return;
            }
            if (EncoderMove == 1) // 上
            {
                u16 nowselect = lv_roller_get_selected(ui_EnrollID1);
                nowselect++;
                if (nowselect > 9)
                    nowselect = 0;
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_roller_set_selected(ui_EnrollID1, nowselect, LV_ANIM_ON);
                    xSemaphoreGive(xMutex_UiChange);
                }

                EncoderMove = 0;
            }
            else if (EncoderMove == 2)
            {
                u16 nowselect = lv_roller_get_selected(ui_EnrollID1);
                if (nowselect <= 0)
                    nowselect = 9;
                else
                    nowselect--;
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_roller_set_selected(ui_EnrollID1, nowselect, LV_ANIM_ON);
                    xSemaphoreGive(xMutex_UiChange);
                }

                EncoderMove = 0;
            }
        }
        else if (Enroll_flag == Enroll_SetID2)
        {
            if (enterPass.KeyType == Key_click || cancelPass.KeyType == Key_click)
            {
                Enroll_select = 0;
                Enroll_flag = Enroll_Null;
                cancelPass.KeyType = Key_none;
                enterPass.KeyType = Key_none;
                return;
            }
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                if (EncoderMove == 1) // 下
                {
                    u16 nowselect = lv_roller_get_selected(ui_EnrollID2);
                    nowselect++;
                    if (nowselect == 9) // 进1
                    {
                        u8 id1 = lv_roller_get_selected(ui_EnrollID1);
                        id1++;
                        if (id1 > 9)
                            id1 = 0;
                        lv_roller_set_selected(ui_EnrollID1, id1, LV_ANIM_ON);
                    }
                    if (nowselect > 9)
                        nowselect = 0;
                    lv_roller_set_selected(ui_EnrollID2, nowselect, LV_ANIM_ON);
                    EncoderMove = 0;
                }
                else if (EncoderMove == 2)
                {
                    u16 nowselect = lv_roller_get_selected(ui_EnrollID2);
                    if (nowselect == 0) // 退1
                    {
                        u8 id1 = lv_roller_get_selected(ui_EnrollID1);
                        if (id1 == 0)
                            id1 = 0;
                        else
                            id1--;
                        lv_roller_set_selected(ui_EnrollID1, id1, LV_ANIM_ON);
                    }
                    if (nowselect <= 0)
                        nowselect = 9;
                    else
                        nowselect--;
                    lv_roller_set_selected(ui_EnrollID2, nowselect, LV_ANIM_ON);
                    EncoderMove = 0;
                }
                xSemaphoreGive(xMutex_UiChange);
            }
        }
        else if (Enroll_flag == Enroll_SetID3)
        {
            if (enterPass.KeyType == Key_click || cancelPass.KeyType == Key_click)
            {
                Enroll_select = 0;
                Enroll_flag = Enroll_Null;
                cancelPass.KeyType = Key_none;
                enterPass.KeyType = Key_none;
                return;
            }
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                if (EncoderMove == 1) // 上
                {
                    u16 nowselect = lv_roller_get_selected(ui_EnrollID3);
                    if (nowselect == 9) // 进1
                    {
                        u8 id2 = lv_roller_get_selected(ui_EnrollID2);
                        id2++;
                        if (id2 > 9)
                        {
                            u8 id1 = lv_roller_get_selected(ui_EnrollID1);
                            id1++;
                            if (id1 > 9)
                                id1 = 0;
                            lv_roller_set_selected(ui_EnrollID1, id1, LV_ANIM_ON);
                            id2 = 0;
                        }

                        lv_roller_set_selected(ui_EnrollID2, id2, LV_ANIM_ON);
                    }
                    nowselect++;
                    if (nowselect > 9)
                        nowselect = 0;
                    lv_roller_set_selected(ui_EnrollID3, nowselect, LV_ANIM_ON);
                    EncoderMove = 0;
                }
                else if (EncoderMove == 2)
                {
                    u16 nowselect = lv_roller_get_selected(ui_EnrollID3);
                    if (nowselect == 0) // 退1
                    {
                        u8 id2 = lv_roller_get_selected(ui_EnrollID2);
                        if (id2 == 0)
                        {
                            u8 id1 = lv_roller_get_selected(ui_EnrollID1);
                            if (id1 == 0)
                                id1 = 0;
                            else
                                id1--;
                            lv_roller_set_selected(ui_EnrollID1, id1, LV_ANIM_ON);
                            id2 = 0;
                        }
                        else
                            id2--;
                        lv_roller_set_selected(ui_EnrollID2, id2, LV_ANIM_ON);
                    }
                    if (nowselect <= 0)
                        nowselect = 9;
                    else
                        nowselect--;
                    lv_roller_set_selected(ui_EnrollID3, nowselect, LV_ANIM_ON);
                    EncoderMove = 0;
                }
                xSemaphoreGive(xMutex_UiChange);
            }
        }
        else if (Enroll_flag == Enroll_Begin)
        {
            u8 sucess_flag = 0;
            if (Enroll_process_flag == Enroll_step_0)
            {
                if (Enrrol_ID < maxusernumber)
                {
                    // 如果有手指按下 且ID数值正确
                    if (TCH == 1 && TCH == Enroll_Raise_flag)
                    {
                        // 录入指纹
                        finger.GetImage();
                        delay(50);
                        res = finger.GenChar(Enroll_Times - Enroll_nowCS + 1); // 录入 Enroll_Times 次  每次自动合成特征
                        // 判断返回值 0为无误
                        if (!res)
                        {                   //
                            Enroll_nowCS--; // 注册次数减一
                            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                            {
                                lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                                delay(500);
                                lv_label_set_text_fmt(ui_LabFingerLine1, "Finger");
                                lv_label_set_text_fmt(ui_LabFingerLine2, "Raise");
                                lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                                Enroll_Bar_var += 10;
                                lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_ON); // 按下进度+
                                xSemaphoreGive(xMutex_UiChange);
                            }

                            delay(800);
                            sucess_flag = 1;
                        }
                        else //
                        {
                            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                            {
                                lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                                delay(500);
                                lv_label_set_text_fmt(ui_LabFingerLine1, "Error");
                                lv_label_set_text_fmt(ui_LabFingerLine2, "Retry");
                                lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                                Enroll_Bar_var = 0;
                                lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_ON);
                                xSemaphoreGive(xMutex_UiChange);
                            }
                            delay(500);
                        }
                        Enroll_Raise_flag = 0;
                    }

                    if (TCH == 2 && Enroll_Raise_flag == 0) // 指纹抬起
                    {
                        delay(500);
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabFingerLine1, "Finger");
                            lv_label_set_text_fmt(ui_LabFingerLine2, "Press");
                            lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                            Enroll_Bar_var += 10;
                            lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_ON); // 按下进度+
                            sucess_flag = 0;
                            Enroll_Raise_flag = 1;
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        if (Enroll_nowCS == 0) // 合成了五次模板
                        {
                            res = finger.RegModel(); // 从Buf区生成模板
                            Enroll_process_flag = Enroll_step_2;
                            lv_bar_set_value(ui_BarEnroll, 100, LV_ANIM_ON);
                            return;
                        }
                    }
                }
                else
                {
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                        xSemaphoreGive(xMutex_UiChange);
                    }
                    delay(500);
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_label_set_text_fmt(ui_LabFingerLine1, "FingerID");
                        lv_label_set_text_fmt(ui_LabFingerLine2, "Error");
                        lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                        xSemaphoreGive(xMutex_UiChange);
                    }
                    delay(1000);
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL); // ID错误，返回上一层
                        xSemaphoreGive(xMutex_UiChange);
                    }

                    // Delayclick(144, 105, 1000);
                    // delay(1000);
                    Enroll_select = 0;
                    Enroll_flag = Enroll_Null;
                }
            }
            else if (Enroll_process_flag == Enroll_step_2) // 尝试存入模板
            {

                if (!res)
                {
                    res = finger.StoreModel(1, Enrrol_ID); // 存入指纹模板
                    if (!res)
                    {
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                            xSemaphoreGive(xMutex_UiChange);
                        }
                        delay(500);
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabFingerLine1, "Registered");
                            lv_label_set_text_fmt(ui_LabFingerLine2, "Succeed");
                            lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        Enroll_process_flag = Enroll_step_3; // 注册完成
                        Enrrol_ID = 0;
                    }
                    else
                    {
                        // 错误则不断循环 直至手动退出
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                            xSemaphoreGive(xMutex_UiChange);
                        }
                        delay(500);
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabFingerLine1, "Registered");
                            lv_label_set_text_fmt(ui_LabFingerLine2, "Error");
                            lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                            lv_bar_set_value(ui_BarEnroll, 0, LV_ANIM_ON);
                            xSemaphoreGive(xMutex_UiChange);
                        }
                        Enrrol_ID = 0;
                    }
                }
            }

            if (cancelPass.KeyType == Key_long) // 在enrrolBegin中 长按 取消键 返回上一层
            {
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL); // 播放面板褪去动画
                    xSemaphoreGive(xMutex_UiChange);
                }

                Enroll_select = 0;
                Enroll_flag = Enroll_Null;     // 返回上一级
                cancelPass.KeyType = Key_none; // 清空标志
                return;
            }
        }
        else if (Enroll_flag == Enroll_Delet)
        {
            if (Enroll_Delet_flag == Enroll_Delet_step_0)
            {
                if (enterPass.KeyType == Key_long)
                {
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                        xSemaphoreGive(xMutex_UiChange);
                    }

                    delay(500);
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_label_set_text_fmt(ui_LabFingerLine1, "EnterDelet");

                        if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabFingerLine2, "%d%d", userIDCode[Enrrol_ID][0], userIDCode[Enrrol_ID][1]);
                            xSemaphoreGive(xMutex_userData);
                        }

                        lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                        Enroll_Bar_var += 25;
                        lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_OFF); // 设置初始值
                        Enroll_Delet_flag = Enroll_Delet_step_1;
                        xSemaphoreGive(xMutex_UiChange);
                    }
                    enterPass.KeyType = Key_none;
                }
            }
            else if (Enroll_Delet_flag == Enroll_Delet_step_1)
            {
                if (enterPass.KeyType == Key_long)
                {
                    res = finger.DeletChar(Enrrol_ID, 1); // 删除一个指纹
                    Enroll_Bar_var += 25;
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_OFF); // 设置初始值
                        xSemaphoreGive(xMutex_UiChange);
                    }

                    if (!res)
                    {
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        delay(500);
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabFingerLine1, "DeletChar");
                            lv_label_set_text_fmt(ui_LabFingerLine2, "Succeed");
                            lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                            Enroll_Bar_var = 100;
                            lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_OFF); // 设置初始值
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        Enrrol_ID = 0;
                        Enroll_Delet_flag = Enroll_Delet_step_2; // 删除完成
                    }
                    else
                    {
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL);
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        delay(500);
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabFingerLine1, "DeletChar");
                            lv_label_set_text_fmt(ui_LabFingerLine2, "Error");
                            lv_event_send(ui_ButtonFingerText, LV_EVENT_CLICKED, NULL);
                            Enroll_Bar_var = 0;
                            lv_bar_set_value(ui_BarEnroll, Enroll_Bar_var, LV_ANIM_OFF); // 设置初始值
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        Enrrol_ID = 0;
                    }

                    enterPass.KeyType = Key_none;
                }
            }

            if (cancelPass.KeyType == Key_long) // 在enrrolBegin中 长按 取消键 返回上一层
            {
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_event_send(ui_enrollfinger, LV_EVENT_CLICKED, NULL); // 播放面板褪去动画
                    xSemaphoreGive(xMutex_UiChange);
                }
                Enroll_select = 0;
                Enroll_flag = Enroll_Null;     // 返回上一级
                cancelPass.KeyType = Key_none; // 清空标志
                return;
            }
        }

        if (Enroll_flag >= Enroll_SetID1 && Enroll_flag <= Enroll_SetID3)
        {
            EnrollScreen_shouIDNum();
        }
    }
}
// time锁 flash锁，finger锁
void Finger_record_worktime(uint8_t id, u8 mod)
{
    if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS && xSemaphoreTake(xMutex_Time, portMAX_DELAY) == pdPASS)
    {

        if (!mod) // mod=0签到
        {
            save_checkinFlash(id, SysClockTime.hh);
            finger_record[id][day].last_h = SysClockTime.hh;
            Serial.printf("checkin id =%d time updata = %d \n", id, SysClockTime.hh);
        }
        else
        {
            if (finger_record[id][day].last_h < SysClockTime.hh)
            { // 正常打卡情况
                // 小时记录
                if (SysClockTime.hh - finger_record[id][day].last_h <= 12)
                {
                    u8 addtime = SysClockTime.hh - finger_record[id][day].last_h;
                    save_TotalWorkTime(id, addtime);
                    finger_record[id][day].worktime += addtime;
                }
                else
                {
                    finger_record[id][day].worktime += 1;
                    save_TotalWorkTime(id, 1); // 俩次打卡时间大于八小时则按1小时算
                }
            }
            else if (finger_record[id][day].last_h > SysClockTime.hh)
            {
                if (24 - (finger_record[id][day].last_h - SysClockTime.hh > 8))
                {
                    finger_record[id][day].worktime += 1;
                    save_TotalWorkTime(id, 1);
                }
                else
                {
                    u8 addtime = 24 - (finger_record[id][day].last_h - SysClockTime.hh);
                    finger_record[id][day].worktime += addtime;
                    save_TotalWorkTime(id, addtime);
                }
            }
            Serial.printf("checkout =%d lasttime = %d worktime = %d\n  ", id, finger_record[id][day].last_h, finger_record[id][day].worktime);
            finger_record[id][day].last_h = SysClockTime.hh;
            save_checkinFlash(id, 0);
        }
        xSemaphoreGive(xMutex_Time);
        xSemaphoreGive(xMutex_fingerData);
    }
}

void Finger_record_settlement()
{
    for (u8 id = 0; id < maxusernumber; id++)
    {
        if (userCheck[id]) // 只有签入，没签出则按一小时算
        {
            u8 addtime = abs(24 - finger_record[id][oldday].last_h);
            if (addtime < 8)
            {
                Serial.printf("\nrecord::new day recalculate %d \n", addtime);
                finger_record[id][oldday].last_h = 0; // 重置打卡时间
                finger_record[id][oldday].worktime += (addtime + 1);
                save_checkinFlash(id, 1);
                save_TotalWorkTime(id, addtime);
                save_workTime(id, oldday);
            }
            else if (addtime != 24)
            {
                Serial.printf("\nrecord::did not chick out %d \n", 1);
                finger_record[id][oldday].last_h = 0;    // 重置打卡时间
                finger_record[id][oldday].worktime += 1; // 超十二小时按一小时算
                save_checkinFlash(id, 0);                // 重置签入时间
                save_TotalWorkTime(id, 1);
                save_workTime(id, oldday);
            }
            userCheck[id] = 0; // 自动签出
        }
    }
}

void Finger_record_worktimeClear() // 每月最后一天晚上十一点结算
{
    if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
    {
        if (MONTH_DAYS[month] == day)
            memset(finger_record, 0, sizeof(finger_record));
        xSemaphoreGive(xMutex_fingerData);
    }
}

u16 getWorktotalTime(uint8_t id)
{
    u16 res = 0;
    if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
    {
        for (u8 i = 1; i <= day; i++)
        {
            res += finger_record[id][i].worktime;
        }
        xSemaphoreGive(xMutex_fingerData);
    }

    return res;
}

// TFT
uint8_t TCP_OpenTime = 0;
void TFT_Close()
{
    nowscr.push(Screen_Close);
    TFT_stateFlag = 0;
    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, LOW);
}
void TFT_Open()
{
    if (nowscr.top() == Screen_Close)
        nowscr.pop();
    TFT_stateFlag = 1;
    // TftLiveTime = SysClockTime.mm;
    Tic_TFTFree.tic();
    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, HIGH);
}
void TFT_Draw(void *parameter)
{

    while (1)
    { // xMutex_UiChange
        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
        {
            if (TFT_stateFlag)
                lv_timer_handler();
            xSemaphoreGive(xMutex_UiChange);
            vTaskDelay(10);
        }
    }
    // Serial.println("TFT_Draw");
    vTaskDelete(NULL);
}

u8 getHostUserIDData()
{
    uint16_t idwz = 0;
    u8 verifyCode = 0;
    if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
    {
        for (uint8_t i = 0; i < 99; i++)
        {
            if (idwz >= tcpmag.DataLen - 1)
            {
                Serial.printf("Data OUT idwz %d len %d", idwz, tcpmag.DataLen);
                break;
            }
            for (uint8_t m = 0; m < 2; m++)
            {
                userIDCode[i][m] = 0;
                for (uint8_t n = 0; n < 4; n++)
                {
                    userIDCode[i][m] |= (tcpmag.dataframe[idwz] << 8 * n);
                    verifyCode += tcpmag.dataframe[idwz] % 10;
                    idwz++;
                }
            }
        }
        xSemaphoreGive(xMutex_userData);
    }

    Serial.printf("\n VERIFYCode %d  datacode %d \n", verifyCode, tcpmag.dataframe[tcpmag.DataLen - 1]);
    if (verifyCode != tcpmag.dataframe[tcpmag.DataLen - 1])
        return 0;
    else
        return 1;
}
u8 megstate = 0;
u8 BarupdataVar = 0;
u8 sendUserWorkData()
{
    if (megstate == 0)
        return 0;
    static u8 verifyCode = 0, indx = 0, workday = 1;
    static u16 datalen = 2;
    if (megstate == 0xff)
    { // 重置
        datalen = 2;
        verifyCode = 0;
        indx = 0;
        workday = 1;
        megstate = 0;
    }
    tcpmag.sendframe[0] = 0XDF;
    tcpmag.sendframe[1] = 0xFD;

    for (; indx < maxusernumber; indx++)
    {

        for (; workday < 32; workday++)
        { // 31
            if (datalen == 1023)
            {
                u8 outtime = 0;
                while (outtime < 5)
                {
                    Serial.printf("main Send ::outtime %d datalen %d \n", outtime, datalen);
                    u8 res = tcpmag.addSendPack(ID_Upper_computer, Cmd_SendWork, datalen, tcpmag.sendframe);
                    if (res == 1) // 等待发送数据
                    {
                        BarupdataVar += 15;
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_bar_set_value(ui_Barupdata, BarupdataVar, LV_ANIM_ON);
                            xSemaphoreGive(xMutex_UiChange);
                        }
                        datalen = 2;
                        return 0; // 发送未完成
                    }
                    else if (res == 3)
                    {
                        megstate = 0xff;
                        datalen = 2;
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_label_set_text_fmt(ui_LabCheckIFO, "SendError");
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        delay(1000);
                        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                        {
                            lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
                            lv_obj_add_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN);
                            xSemaphoreGive(xMutex_UiChange);
                        }

                        Serial.println("main Send :: send Error3");
                        return 3; // 发送错误
                    }
                    else
                    {
                        Serial.println("main Send :: waiting Send");
                    }
                    delay(500);
                    outtime++;
                };
                if (outtime >= 5)
                {
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_label_set_text_fmt(ui_LabCheckIFO, "SendError");
                        xSemaphoreGive(xMutex_UiChange);
                    }

                    delay(1000);
                    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                    {
                        lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
                        lv_obj_add_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN);
                        xSemaphoreGive(xMutex_UiChange);
                    }

                    megstate = 0xff;
                    datalen = 2;
                    Serial.println("main Send :: send Error1");
                    return 0;
                }
            }
            if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
            {
                tcpmag.sendframe[datalen] = finger_record[indx][workday].worktime;
                xSemaphoreGive(xMutex_fingerData);
            }

            verifyCode += tcpmag.sendframe[datalen] % 10;
            datalen++;
        }
        workday = 1;
    }
    tcpmag.sendframe[0] = 0XBA;
    tcpmag.sendframe[1] = 0xAB;
    tcpmag.sendframe[datalen] = verifyCode;
    datalen++;

    if (BarupdataVar < 80)
        BarupdataVar = 80;
    if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
    {
        lv_bar_set_value(ui_Barupdata, BarupdataVar, LV_ANIM_ON);
        xSemaphoreGive(xMutex_UiChange);
    }

    Serial.printf("main finish ::data len %d verifyCode %d ", datalen, verifyCode);
    u8 outtime = 0;
    while (outtime < 5)
    {
        u8 res = tcpmag.addSendPack(ID_Upper_computer, Cmd_SendWork, datalen, tcpmag.sendframe);
        if (res == 1) // 等待发送数据
        {
            indx = 0;
            megstate = 0;
            workday = 1;
            datalen = 2;
            verifyCode = 0;
            BarupdataVar = 0;
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_bar_set_value(ui_Barupdata, 100, LV_ANIM_ON);
                xSemaphoreGive(xMutex_UiChange);
            }

            Serial.printf("data Send OK\n\n");
            print_data();
            delay(800);

            save_EEProm(0);          // 保存单日工作时长
            save_Time();             // 保存现在时间
            save_WorkTime_BK();      // 保存单日时长备份
            save_BK_TotalWorkTime(); // 保存总时长备份

            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
                lv_obj_add_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN);
                xSemaphoreGive(xMutex_UiChange);
            }

            return 1; // 发送完成
        }
        else if (res == 3)
        {
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_label_set_text_fmt(ui_LabCheckIFO, "SendError");
                xSemaphoreGive(xMutex_UiChange);
            }

            delay(1000);
            if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
            {
                lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
                lv_obj_add_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN);
                xSemaphoreGive(xMutex_UiChange);
            }

            BarupdataVar = 0;
            indx = 0;
            megstate = 0xff;
            workday = 1;
            datalen = 2;
            verifyCode = 0;
            Serial.println("main Send finish:: send Error3");
            return 3; // 发送错误
        }
        else
        {
            Serial.println("main Send :: waiting Send");
        }
        delay(500);
        outtime++;
    };
    if (outtime >= 5)
    {
        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
        {
            lv_label_set_text_fmt(ui_LabCheckIFO, "SendError");
            xSemaphoreGive(xMutex_UiChange);
        }

        delay(1000);
        if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
        {
            lv_event_send(ui_PanelCheck, LV_EVENT_CLICKED, NULL);
            lv_obj_add_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN);
            xSemaphoreGive(xMutex_UiChange);
        }

        BarupdataVar = 0;
        indx = 0;
        megstate = 0xff;
        workday = 1;
        datalen = 2;
        verifyCode = 0;
        Serial.println("main Send :: send Error2");
    }
    BarupdataVar = 0;
}

void TCP_Message(void *parameter)
{
    static u8 flagggg = 0;
    while (1)
    {

        if (WIFI_Open_Flag >= 2)
        {
            if (WIFI_Open_Flag == 3)
            {
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                continue;
            }
            Serial.println("WIFI All Close");
            WiFi.disconnect();
            WiFi.mode(WIFI_OFF);
            WiFi.setSleep(true);
            if (flagggg == 0)
            {
                lv_obj_clear_state(ui_hostcheck, LV_STATE_CHECKED);
                Serial.println("WIFI ERROR");
                flagggg = 1;
            }
            WIFI_Open_Flag = 3;
        }

        if ((WIFI_Open_Flag == 0 && WiFi.status() != WL_CONNECTED) || WIFI_ReConnectFlag == 0)
        {
            connectNet();
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }

        if (flagggg)
        {
            Serial.println("MEG RUN");
            lv_obj_add_state(ui_hostcheck, LV_STATE_CHECKED);
            flagggg = 0;
        }
        tcpmag.connectLoop();
        if (tcpmag.Recvflag)
        {
            if (tcpmag.command[2] == 0xA1) // 上位机，发送用户数据
            {
                if (getHostUserIDData())
                {
                    TFT_Open();
                    save_EEProm(1);
                    read_EEProm();
                    print_data();
                }
                else
                {
                    Serial.println("Data verifyCode Error");
                };
                tcpmag.Recvflag = 0;
                tcpmag.command[2] = 0;
            }
            else if (tcpmag.command[2] == 0xA2) // 上位机命令，要求发送工作时长
            {
                megstate = 1;
                TFT_Open();
                if (xSemaphoreTake(xMutex_UiChange, portMAX_DELAY) == pdPASS)
                {
                    lv_label_set_text_fmt(ui_LabWorkTime, "");
                    lv_label_set_text_fmt(ui_LabStudyID, "");
                    lv_label_set_text_fmt(ui_LabCheckIFO, "Sending");
                    lv_obj_clear_flag(ui_Barupdata, LV_OBJ_FLAG_HIDDEN); // 取消隐藏
                    BarupdataVar = 10;
                    lv_bar_set_value(ui_Barupdata, BarupdataVar, LV_ANIM_ON); // 设置值
                    lv_event_send(ui_ButtonCheck, LV_EVENT_CLICKED, NULL);
                    xSemaphoreGive(xMutex_UiChange);
                }

                Serial.println("begin sendUserWorkData");
                tcpmag.Recvflag = 0;
                tcpmag.command[2] = 0;
            }
        }
        vTaskDelay(5);
    }
    // Serial.println("TFT_Draw");
    vTaskDelete(NULL);
}
StopWatch Tic_Heartbeat;
void TCP_Send(void *parameter)
{
    u8 oldsendtimess = 0;

    oldsendtimess = SysClockTime.ss;

    Tic_Heartbeat.tic();
    u8 flagggg = 0;
    while (1)
    {
        if (WIFI_Open_Flag == 1)
        {
            Serial.println("WIFI Send Close");
            WIFI_Open_Flag = 2;
        }
        if (WIFI_Open_Flag > 0 || WiFi.status() != WL_CONNECTED)
        {
            if (flagggg == 0)
            {
                lv_obj_clear_state(ui_hostcheck, LV_STATE_CHECKED);
                Serial.println("WIFI ERROR");
                flagggg = 1;
            }
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }

        if (flagggg)
        {
            Serial.println("WIFI RUN");
            lv_obj_add_state(ui_hostcheck, LV_STATE_CHECKED);
            flagggg = 0;
        }

        tcpmag.Send();
        if (Tic_Heartbeat.toc() > 2500 || tcpmag.Recvrespond)
        {
            tcpmag.HeartbeatLoop();
            Tic_Heartbeat.tic();
            tcpmag.Recvrespond = 0;
        }
        vTaskDelay(10);
    }
    vTaskDelete(NULL);
}

void print_data()
{
    for (uint8_t i = 0; i < maxusernumber; i++)
    {
        Serial.printf("id =%d :", i);
        for (uint8_t w = 1; w < 32; w++)
        { // 31
            Serial.printf("%d  ", finger_record[i][w].worktime);
        }
        Serial.println("");
    }
    Serial.println("this userIDcode\n");
    for (uint8_t i = 0; i < maxusernumber; i++)
    {
        for (uint8_t w = 0; w < 2; w++)
        { // 31
            if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
            {
                Serial.printf("%d   \t", userIDCode[i][w]);
                xSemaphoreGive(xMutex_userData);
            }
        }
    }
}
// 时间锁，flash锁
void read_Time()
{

    uint16_t eeprom_wz = 4000; // 4000 -4010
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS && xSemaphoreTake(xMutex_Time, portMAX_DELAY) == pdPASS)
    {
        month = EEPROM.read(eeprom_wz++);
        day = EEPROM.read(eeprom_wz++);
        oldday = EEPROM.read(eeprom_wz++);
        SysClockTime.hh = EEPROM.read(eeprom_wz++);
        SysClockTime.mm = EEPROM.read(eeprom_wz++);
        xSemaphoreGive(xMutex_EEprom);
    }

    Serial.printf("save Time : month %d day %d oldday %d hh %d mm %d \n", month, day, oldday, SysClockTime.hh, SysClockTime.mm);
    xSemaphoreGive(xMutex_Time);
}
// flash锁 time锁
void save_Time()
{
    uint16_t eeprom_wz = 4000;
    Serial.printf("\nsave Time:: loop in\n");
    if (xSemaphoreTake(xMutex_Time, portMAX_DELAY) == pdPASS)
    {
        if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
        {
            EEPROM.updata(eeprom_wz++, month);
            EEPROM.updata(eeprom_wz++, day);
            EEPROM.updata(eeprom_wz++, oldday);
            EEPROM.updata(eeprom_wz++, SysClockTime.hh);
            EEPROM.updata(eeprom_wz++, SysClockTime.mm);
            EEPROM.commit();
            xSemaphoreGive(xMutex_EEprom);
        }
        Serial.printf("save Time : month %d day %d oldday %d hh %d mm %d \n", month, day, oldday, SysClockTime.hh, SysClockTime.mm);
        xSemaphoreGive(xMutex_Time);
    }
}

void save_workTime(uint8_t id, uint8_t tday)
{
    uint16_t idex = 0;
    uint32_t eeprom_wz = 0;
    // 更新校验码
    u8 vcode = 0;
    for (uint8_t i = 0; i < maxusernumber; i++)
    {
        for (uint8_t w = 1; w < 32; w++)
        {
            vcode += finger_record[i][w].worktime;
        }
    }
    if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
    {
        idex = id * 31 + tday - 1;
        if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
            EEPROM.updata(idex, finger_record[id][tday].worktime);
        EEPROM.updata(1549, vcode);
        EEPROM.commit();
        xSemaphoreGive(xMutex_fingerData);
    }
    xSemaphoreGive(xMutex_EEprom);
}

void save_EEProm(uint8_t mod)
{
    Serial.printf("\nsave EEProm Begin\n");
    // save finger_record 0
    uint32_t eeprom_wz = 0;
    if (mod == 0) // 0 - 1549 WORK MAX 1519  1550 - 3099 WORK_BK MAX 3069
    {
        Serial.println("\nsave_EEProm:: lopp in mod 1\n");
        if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
        {
            u8 vcode = 0;
            if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
            {
                for (uint8_t i = 0; i < maxusernumber; i++)
                {
                    for (uint8_t w = 1; w < 32; w++)
                    { // 31   (id*31+day)-1
                        EEPROM.updata(eeprom_wz, finger_record[i][w].worktime);
                        vcode += finger_record[i][w].worktime;
                        eeprom_wz++;
                    }
                } // 1240

                xSemaphoreGive(xMutex_fingerData);
            }
            EEPROM.updata(1549, vcode);
            EEPROM.commit();
            xSemaphoreGive(xMutex_EEprom);
        }
        Serial.println("\nsave_EEProm:: lopp out\n");
    }
    else if (mod == 1)
    {
        Serial.println("\nsave_EEProm:: lopp in mod 2\n"); // 3100 - 3500   3100 - 3492
        if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
        {
            eeprom_wz = 3100;
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                for (uint8_t w = 0; w < 2; w++)
                {
                    for (uint8_t p = 0; p < 4; p++)
                    {
                        if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
                        {
                            EEPROM.updata(eeprom_wz, ((userIDCode[i][w] >> (p * 8)) & (0xff)));
                            xSemaphoreGive(xMutex_userData);
                        }
                        eeprom_wz++;
                    }
                }
            }
            EEPROM.commit();
            xSemaphoreGive(xMutex_EEprom);
        }
        Serial.println("\nsave_EEProm:: lopp out\n");
        Serial.printf("eeprom_wz = %d  :\n", eeprom_wz); // save userIDCode 3100 -3900
    }
}
void save_WorkTime_BK() // 1550 - 3100 WORK_BK
{
    uint32_t eeprom_wz = 1550;
    u8 vcode = 0;
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
        {
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                for (uint8_t w = 1; w <= day; w++)
                { // 31   (id*31+day)-1

                    EEPROM.updata(eeprom_wz, finger_record[i][w].worktime);
                    vcode += finger_record[i][w].worktime;
                    eeprom_wz++;
                }

                for (uint8_t w = day + 1; w < 32; w++)
                { // 31   (id*31+day)-1
                    EEPROM.updata(eeprom_wz, 0);
                    eeprom_wz++;
                }
            } // 2790

            xSemaphoreGive(xMutex_fingerData);
        }
        EEPROM.updata(3099, vcode);
        EEPROM.commit();
        Serial.printf("\nEEPROM::WorkTimeBK Save Scceed \n");
        xSemaphoreGive(xMutex_EEprom);
    }
}

u8 read_WorkTime_BK() // 1550 - 3100 WORK_BK
{
    uint32_t eeprom_wz = 1550;
    u8 vcode = 0, rcode = 0;
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
        {
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                for (uint8_t w = 1; w < 32; w++)
                { // 31
                    finger_record[i][w].worktime = EEPROM.read(eeprom_wz);
                    vcode += finger_record[i][w].worktime;
                    eeprom_wz++;
                }
            }
            xSemaphoreGive(xMutex_fingerData);
        }

        rcode = EEPROM.read(3099);
        xSemaphoreGive(xMutex_EEprom);
    }

    if (rcode != vcode)
    {
        Serial.printf("\nEEPROM:: Read WorkTime_BK ERROR \n");
        eeprom_wz = 1550;
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            for (uint8_t w = 1; w < 32; w++)
            {
                EEPROM.updata(eeprom_wz++, 0);
            }
        }
        return 0;
    }
    Serial.printf("\n EEPROM:: Read WorkTime_BK Succeed \n");
    return 1;
}

void read_EEProm()
{
    uint32_t eeprom_wz = 0; // 0- 1549 worktime
    u8 vcode = 0, rcode = 0;
    u8 ycflag = 0;
    uint32_t all_workTime0 = 0;
    uint32_t all_workTime1 = 0;
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
        {
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                for (uint8_t w = 1; w < 32; w++)
                {                                                          // 31
                    finger_record[i][w].worktime = EEPROM.read(eeprom_wz); // 读取单日时长
                    eeprom_wz++;
                    all_workTime0 += finger_record[i][w].worktime;
                    vcode += finger_record[i][w].worktime;
                    if (finger_record[i][w].worktime > 24) // 单日时长大于24则异常
                    {
                        ycflag = 1;
                        break;
                    }
                }
            }
            xSemaphoreGive(xMutex_fingerData);
        }

        rcode = EEPROM.read(1549);
        xSemaphoreGive(xMutex_EEprom);
    }

    read_checkinFlash(); // 已加锁：读取签入时间

    u8 readflag = 0;

    if (rcode != vcode) // 验证码错误读取工作时长备份
    {
        Serial.printf("\nEEPROM:: Read WorkTime ERROR \n");

        if (!read_WorkTime_BK()) // 工作时长备份验证码错误，读取总工作时长
        {
            readflag = 1; // 说明读取过总时长

            if (read_TotalWorkTime())
            {                 // 读取总工作时长和备份总工作时长，成功则写入
                readflag = 2; // 总时长读取成功
                if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
                {
                    for (uint8_t i = 0; i < maxusernumber; i++)
                    {
                        u16 temptime = TotalWorkTime[i];
                        for (uint8_t w = 1; w < 32; w++)
                        { // 31

                            if (temptime > 6)
                            {
                                finger_record[i][w].worktime = 6;
                                temptime -= 6;
                            }

                            else
                            {
                                finger_record[i][w].worktime = temptime;
                                temptime = 0;
                            }
                        }
                    }
                    xSemaphoreGive(xMutex_fingerData);
                }
            }
            else // 都失败则清0
            {
                if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
                {
                    eeprom_wz = 0;
                    for (uint8_t i = 0; i < maxusernumber; i++)
                    {
                        for (uint8_t w = 1; w < 32; w++)
                        {
                            EEPROM.updata(eeprom_wz++, 0);
                            finger_record[i][w].worktime = 0;
                        }
                    }
                    xSemaphoreGive(xMutex_fingerData);
                }
                readflag = 0;
                Serial.printf("\n \n EEPROM:: Read Flash ALL ERROR!!!! \n \n");
            }
        };
    }
    else
    {
        Serial.printf("\nEEPROM:: Read WorkTime Succeed \n");
        syn_WorkTotalTime();
    }

    if (readflag == 0) // 未初始化总时长，则初始化一次
    {
        if (read_TotalWorkTime())
            readflag = 2; // 总时长读取成功
        else
            readflag = 1;
    }

    for (uint8_t i = 0; i < maxusernumber; i++)
    {
        all_workTime1 += TotalWorkTime[i];
    }
    // 总时长读取成功，但总时长大于工作时长时取总时长
    if ((readflag == 2 && all_workTime1 > all_workTime0) || ycflag == 1)
    {
        Serial.printf("\nEEPROM:: Read WorkTime Succeed \n");
        Serial.printf("\nEEPROM:: Begin synchronous  Total Time TO Work Time \n");
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            int temptime = TotalWorkTime[i];
            for (uint8_t w = 1; w < 32; w++)
            { // 31

                if (temptime > 12)
                {
                    finger_record[i][w].worktime = 12;
                    temptime -= 12;
                }
                else
                {
                    finger_record[i][w].worktime = temptime;
                    temptime = 0;
                }
            }
        }
        save_WorkTime_BK();
        save_EEProm(0);
    }

    Serial.printf("eeprom_wz = %d  :\n", eeprom_wz);

    // save userIDCode 3100 -3500
    eeprom_wz = 3100;
    if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
    {
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            for (uint8_t w = 0; w < 2; w++)
            {
                userIDCode[i][w] = 0;
                for (uint8_t p = 0; p < 4; p++)
                {
                    userIDCode[i][w] |= (EEPROM.read(eeprom_wz) << (8 * p));
                    eeprom_wz++;
                }
            }
        }
        xSemaphoreGive(xMutex_userData);
    }

    Serial.printf("eeprom_wz end = %d  :\n", eeprom_wz);
}

// flash签到时间读取
void read_checkinFlash()
{
    uint32_t eeprom_wz = 3500; // 签到时间写入flash  3500 - 3550
    Serial.printf("\ncheckin_EEProm:: read in %d\n", day);
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        if (xSemaphoreTake(xMutex_fingerData, portMAX_DELAY) == pdPASS)
        {
            for (uint8_t i = 0; i < maxusernumber; i++)
            {
                finger_record[i][day].last_h = EEPROM.read(eeprom_wz);

                if (finger_record[i][day].last_h > 24 || finger_record[i][day].last_h == 0) // 数值异常或flash时间为0则定为已签退
                {
                    userCheck[i] = 0;
                    finger_record[i][day].last_h = 0;
                }
                else
                {
                    userCheck[i] = 1; // 断电前未签退
                }
                Serial.printf("\nid %d h %d  \t", i, finger_record[i][day].last_h);
                eeprom_wz++;
            }
            xSemaphoreGive(xMutex_fingerData);
        }
        Serial.println("\ncheckin_EEProm:: read out\n");
        xSemaphoreGive(xMutex_EEprom);
    }
}
// 签到时间写入flash ，签退清0 flash锁
void save_checkinFlash(u8 id, u8 time)
{

    if (id > maxusernumber)
        return;
    uint32_t eeprom_wz = 3500; // 签到时间写入flash  3500 - 3550
    Serial.printf("\ncheckin_EEProm:: save in %d\n", time);
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        EEPROM.updata(eeprom_wz + id, time);
        EEPROM.commit();
        Serial.printf("\ncheckin_EEProm:: save out %d \n", time);
        xSemaphoreGive(xMutex_EEprom);
    }
}

void init_EEPROM()
{
    Serial.printf("\ninit_EEPROM::loop in \n");
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        for (uint16_t i = 0; i < 4096; i++)
        {
            EEPROM.updata(i, 0);
        }
        EEPROM.commit();
        Serial.printf("\ninit_EEPROM::TotalTime Is Succeed \n");
        xSemaphoreGive(xMutex_EEprom);
    }
}
// 读取总工作时长
u8 read_TotalWorkTime() // 上电读取一次
{
    uint32_t eeprom_wz = 3550; // 总工作时长写入flash  3550 - 3650
    u8 vcode = 0, rcode = 0;   // 校验码
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {

        for (uint8_t i = 0; i < maxusernumber; i++) // 高八位 低八位
        {
            TotalWorkTime[i] = EEPROM.read(eeprom_wz++);
            TotalWorkTime[i] << 8;
            TotalWorkTime[i] |= EEPROM.read(eeprom_wz++);
            vcode += (TotalWorkTime[i] & 0xff);
        }
        rcode = EEPROM.read(3640);
        xSemaphoreGive(xMutex_EEprom);
    }

    if (rcode != vcode)
    {
        Serial.printf("\nEEPROM::TotalTime Is Error \n");
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            TotalWorkTime[i] = 0;
        }
        if (read_BK_TotalWorkTime()) // 验证码错误读取总工作时长
            return 1;
        return 0;
    }
    else
        Serial.printf("\nEEPROM::TotalTime Read Scceed \n");
    return 1;
}

// 写入总工作时长 flash锁
void save_TotalWorkTime(u8 id, u8 addtime)
{
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        uint32_t eeprom_wz = 3550; // 总工作时长写入flash  3550 - 3650
        TotalWorkTime[id] += addtime;
        EEPROM.updata(eeprom_wz + id * 2, (TotalWorkTime[id] >> 8) & 0xff);
        EEPROM.updata(eeprom_wz + id * 2 + 1, TotalWorkTime[id] & 0xff);
        u8 vcode = 0;
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            vcode += (TotalWorkTime[i] & 0xff);
        }
        EEPROM.updata(3640, vcode);
        EEPROM.commit();
        xSemaphoreGive(xMutex_EEprom);
    }
}

void save_BK_TotalWorkTime() // 写入总时长备份，每小时一次
{
    uint32_t eeprom_wz = 3650; // 总工作时长备份写入flash  3650 - 3750
    u8 vcode = 0;
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {

        for (uint8_t i = 0; i < maxusernumber; i++) // 高八位 低八位
        {
            EEPROM.updata(eeprom_wz++, (TotalWorkTime[i] >> 8) & 0xff);
            EEPROM.updata(eeprom_wz++, TotalWorkTime[i] & 0xff);
            vcode += (TotalWorkTime[i] & 0xff);
        }
        EEPROM.updata(3740, vcode);
        EEPROM.commit();
        Serial.printf("\nEEPROM::TotalTime_BK Save Scceed \n");
        xSemaphoreGive(xMutex_EEprom);
    }
}

u8 read_BK_TotalWorkTime()
{
    uint32_t eeprom_wz = 3650; // 总工作时长备份写入flash  3650 - 3750
    u8 vcode = 0, rcode = 0;
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {
        // 校验码
        for (uint8_t i = 0; i < maxusernumber; i++) // 高八位 低八位
        {
            TotalWorkTime[i] = EEPROM.read(eeprom_wz++);
            TotalWorkTime[i] << 8;
            TotalWorkTime[i] |= EEPROM.read(eeprom_wz++);
            vcode += (TotalWorkTime[i] & 0xff);
        }
        rcode = EEPROM.read(3740);
        xSemaphoreGive(xMutex_EEprom);
    }

    if (rcode != vcode)
    {
        Serial.printf("\nEEPROM::TotalTime_BK Is Error \n");
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            TotalWorkTime[i] = 0;
        }
        return 0;
    }
    else
        Serial.printf("\nEEPROM::TotalTime_BK Read Scceed \n");
    return 1;
}
void print_worktime()
{
    for (uint8_t i = 0; i < maxusernumber; i++)
    {
        Serial.printf("id = %d  ", i);
        for (uint8_t d = 0; d < 32; d++)
        {
            Serial.printf("%d  ", finger_record[i][d].worktime);
        }
        Serial.println();
    }
}
void print_userIDCode()
{
    if (xSemaphoreTake(xMutex_userData, portMAX_DELAY) == pdPASS)
    {
        for (uint8_t i = 0; i < maxusernumber; i++)
        {
            for (uint8_t m = 0; m < 2; m++)
            {
                Serial.printf("%d  ", userIDCode[i][m]);
            }

            if (i != 0 & i % 20 == 0)
            {
                Serial.println();
            }
        }
        xSemaphoreGive(xMutex_userData);
    }
}

void CheckOut_All_User()
{
    Serial.printf("\nCheckOut::day %d \n", day);
    for (u8 id = 0; id < maxusernumber; id++)
    {

        if (userCheck[id]) // 只有签入，没签出则按一小时算
        {
            u8 addtime = abs(SysClockTime.hh - finger_record[id][day].last_h);
            Serial.printf("\nCheckOut_All_User0:: id %d add %d\n", id, addtime);
            if (addtime >= 12)
                addtime = 0;
            finger_record[id][day].last_h = 0; // 重置打卡时间
            finger_record[id][day].worktime += (addtime);
            Serial.printf("\nCheckOut_All_User1:: %d \n", addtime);
            save_TotalWorkTime(id, addtime);
            save_workTime(id, day);
            save_checkinFlash(id, 0);
            userCheck[id] = 0; // 自动签出
        }
    }
    save_EEProm(0);
}

void syn_WorkTotalTime()
{
    for (uint8_t i = 0; i < maxusernumber; i++) // 高八位 低八位
    {
        TotalWorkTime[i] = getWorktotalTime(i);
    }

    uint32_t eeprom_wz = 3550; // 总工作时长备份写入flash  3650 - 3650
    u8 vcode = 0;
    if (xSemaphoreTake(xMutex_EEprom, portMAX_DELAY) == pdPASS)
    {

        for (uint8_t i = 0; i < maxusernumber; i++) // 高八位 低八位
        {
            EEPROM.updata(eeprom_wz++, (TotalWorkTime[i] >> 8) & 0xff);
            EEPROM.updata(eeprom_wz++, TotalWorkTime[i] & 0xff);
            vcode += (TotalWorkTime[i] & 0xff);
        }
        EEPROM.updata(3640, vcode);
        EEPROM.commit();
        Serial.printf("\nEEPROM::TotalTime Save Scceed \n");
        xSemaphoreGive(xMutex_EEprom);
    }
}
void print_TotalWorkTime()
{
    for (uint8_t i = 0; i < maxusernumber; i++) // 高八位 低八位
    {
        Serial.printf("%d  ", TotalWorkTime[i]);
        if (i != 0 & i % 20 == 0)
        {
            Serial.println();
        }
    }
}